package com.dxy.lc.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.dxy.lc.model.bean.TreeVo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 树工具类
 * 使用方法：
 * （1）如果不用过滤，直接返回
 *      return treeUtil.buildTree();
 * （2）如果要过滤，则先构造树，再调用获取过滤后的树的方法
 *      treeUtil.buildTree();
 *      return treeUtil.getFilteredTree(Set<String> authNodeIdSet)
 * @param <T>
 */
public class TreeUtil<T> {

    private List<TreeVo> list; // 数据列表（一般是数据库查询出来的全量数据）
    private Map<String, TreeVo> map; // 数据视图（key为节点的Id）
    private List<TreeVo> tree; // 树结构

    public TreeUtil(List<TreeVo> treeVoList) {
        Assert.notEmpty(treeVoList, "list不能为空");
        this.list = treeVoList;
        this.map = this.list.stream().collect(Collectors.toMap(TreeVo::getId, Function.identity()));
    }

    public TreeUtil(List<T> beanList, Function<T, TreeVo> func) {
        Assert.notEmpty(beanList, "list不能为空");
        this.list = beanList.stream().map(func).collect(Collectors.toList());
        this.map = this.list.stream().collect(Collectors.toMap(TreeVo::getId, Function.identity()));
    }

    /**
     * 构造树
     *
     * @return
     */
    public List<TreeVo> buildTree() {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        tree = new ArrayList<>();
        // 建立父子关系
        for (TreeVo node : list) {
            String parentId = node.getParentId();
            TreeVo parentNode = null;
            // 如果parentId为null，或者获取不到parent节点则视为顶级节点
            if (parentId == null || (parentNode = map.get(parentId)) == null) {
                tree.add(node);
            } else {
                parentNode.addChild(node);
            }
        }
        // 对 treeList 中的菜单进行排序
        sortNode(tree);
        return tree;
    }

    /**
     * 节点排序
     *
     * @param treeList
     */
    private static void sortNode(List<TreeVo> treeList) {
        treeList.sort(Comparator.comparingInt(TreeVo::getSort));
        for (TreeVo node : treeList) {
            if (node.isHasChildren()) {
                sortNode(node.getChildList());
            }
        }
    }

    /**
     * 设置节点可见性
     *
     * @param authNodeIdSet
     */
    private void setNodeVisible(Set<String> authNodeIdSet) {
        if (CollUtil.isEmpty(authNodeIdSet)) {
            return;
        }

        for (TreeVo treeVo : list) {
            // 授权节点Id集合不包含的视为不可见
            treeVo.setVisible(authNodeIdSet.contains(treeVo.getId()));

            // 当某个节点可见时，循环把它上面的所有上级节点都设为可见
            if (treeVo.isVisible()) {
                String parentId = treeVo.getParentId();
                TreeVo parentNode = null;
                while (StrUtil.isNotEmpty(parentId)
                        && (parentNode = map.get(parentId)) != null) {
                    parentNode.setVisible(true);
                    parentId = parentNode.getParentId();
                }
            }
        }

    }

    /**
     * 获取过滤后的树
     *
     * @param authNodeIdSet
     * @return
     */
    public List<TreeVo> getFilteredTree(Set<String> authNodeIdSet) {
        if (CollUtil.isEmpty(authNodeIdSet)) {
            return Collections.emptyList();
        }
        setNodeVisible(authNodeIdSet);
        removeNotVisibleNode(tree);
        return tree;
    }

    /**
     * 移除不可见的节点
     *
     * @param treeList
     */
    private static void removeNotVisibleNode(List<TreeVo> treeList) {
        Iterator<TreeVo> it = treeList.iterator();
        while (it.hasNext()) {
            TreeVo node = it.next();
            if (!node.isVisible()) {
                it.remove();// 使用迭代器删除（否则会报并发修改异常）
            } else {
                if (node.isHasChildren()) {
                    removeNotVisibleNode(node.getChildList());
                }
            }
        }
    }

    public static void main(String[] args) {
        // 数据库全量字典表数据

    }
}
